﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using System.ComponentModel;
using PSE.Framework.UI.Settings;
using PSE.Framework.ProviderLoader.Configuration;
using PSE.Framework.Configuration;
using PSE.Framework.ProviderLoader;

namespace PSE.Framework.UI.Controls.SmartClient
{
    /// <summary>
    /// Classe para o componente Zoom, que provê input de entrada para o zoom da janela.
    /// O usuário pode usar botões com valores pré-definidos, pode usar um slider ou pode digitar o zoom desejado.
    /// </summary>
    public class Zoom : Control
    {
		Int16TextBox text;

        /// <summary>
        /// Constante com o nome da propriedade da Window responsável por manter o valor do zoom
        /// </summary>
        private const string ZOOM_PROPERTY_NAME = "WindowZoom";

        /// <summary>
        /// Constante para indicar zoom da janela não definido para as operações de salvar/carregar configurações da janela
        /// </summary>
        private const int ZOOM_NOT_DEFINED = -1;

        /// <summary>
        /// Nome do provider para lidar com local settings
        /// </summary>
        private const string LOCAL_SETTINGS_HANDLER_PROVIDER = "localSettingsHandlerProvider";

        /// <summary>
        /// Construtor da classe. Adiciona os eventos para os botões.
        /// </summary>
        public Zoom()
        {
            CommandBindings.Add(new CommandBinding(ZoomCommands.Percent200, ZoomButtonClick));
            CommandBindings.Add(new CommandBinding(ZoomCommands.Percent150, ZoomButtonClick));
            CommandBindings.Add(new CommandBinding(ZoomCommands.Percent100, ZoomButtonClick));
            CommandBindings.Add(new CommandBinding(ZoomCommands.Percent75, ZoomButtonClick));
            CommandBindings.Add(new CommandBinding(ZoomCommands.Percent50, ZoomButtonClick));
            CommandBindings.Add(new CommandBinding(ZoomCommands.Restore, RestoreClick));

            this.MouseEnter += new MouseEventHandler(Zoom_MouseEnter);
            this.MouseLeave += new MouseEventHandler(Zoom_MouseLeave);
            this.Loaded += new RoutedEventHandler(Zoom_Loaded);
        }

        /// <summary>
        /// Delegate para LostFocus no texto de digitação do zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void LostFocusZoomTextHandler(object sender, RoutedEventArgs e);

        /// <summary>
        /// Evento para LostFocus no texto de digitação do zoom.
        /// Trata-se de um evento do Zoom, que basicamente repassa o LostFocus do PART_Text.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public event LostFocusZoomTextHandler LostFocusZoomText;

		/// <summary>
		/// Delegate para ZoomChange que acontece quando o zoom é alterado
		/// </summary>
		public delegate void ZoomChangedHandler(ZoomChangedEventArgs e);

		/// <summary>
		/// Evento para ZoomChange
		/// </summary>
		public event ZoomChangedHandler ZoomChanged;

        /// <summary>
        /// Delegate para GotFocus no texto de digitação do zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void GotFocusZoomTextHandler(object sender, RoutedEventArgs e);

        /// <summary>
        /// Evento para GotFocus no texto de digitação do zoom.
        /// Trata-se de um evento do Zoom, que basicamente repassa o GotFocus do PART_Text.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public event GotFocusZoomTextHandler GotFocusZoomText;

        /// <summary>
        /// Propriedade dependente para o valor atual de zoom definido pelo controle.
        /// </summary>
        public static DependencyProperty CurrentZoomProperty = DependencyProperty.Register("CurrentZoom", typeof(int), typeof(Zoom));

        /// <summary>
        /// Propriedade para o valor atual de zoom definido pelo controle.
        /// </summary>
        public int CurrentZoom
        {
            get
            {
                return (int)GetValue(CurrentZoomProperty);
            }
            set
            {
                SetValue(CurrentZoomProperty, value);
            }
        }

        /// <summary>
        /// Propriedade dependente para o valor atual de zoom definido pelo controle.
        /// </summary>
        public static DependencyProperty ShowRestoreProperty = DependencyProperty.Register("ShowRestore", typeof(bool), typeof(Zoom), new PropertyMetadata(true));

        /// <summary>
        /// Propriedade para o valor atual de zoom definido pelo controle.
        /// </summary>
        public bool ShowRestore
        {
            get
            {
                return (bool)GetValue(ShowRestoreProperty);
            }
            set
            {
                SetValue(ShowRestoreProperty, value);
            }
        }

        /// <summary>
        /// Propriedade dependente para indicar se o controle de zoom deve continuar sendo exibido ou não
        /// </summary>
        public static DependencyProperty KeepShowingProperty = DependencyProperty.Register("KeepShowing", typeof(bool), typeof(Zoom));

        /// <summary>
        /// Propriedade para indicar se o controle de zoom deve continuar sendo exibido ou não
        /// </summary>
        public bool KeepShowing
        {
            get
            {
                return (bool)GetValue(KeepShowingProperty);
            }
            set
            {
                SetValue(KeepShowingProperty, value);
            }
        }

        /// <summary>
        /// Propriedade dependente para indicar se há foco no campo texto para digitação do zoom
        /// </summary>
        public static DependencyProperty IsZoomTextFocusedProperty = DependencyProperty.Register("IsZoomTextFocused", typeof(bool), typeof(Zoom));

        /// <summary>
        /// Propriedade para indicar se há foco no campo texto para digitação do zoom
        /// </summary>
        public bool IsZoomTextFocused
        {
            get
            {
                return (bool)GetValue(IsZoomTextFocusedProperty);
            }
            set
            {
                SetValue(IsZoomTextFocusedProperty, value);
            }
        }

        /// <summary>
        /// Evento de loaded da window. Algumas operações precisam estar aqui, ao invés de estarem no construtor,
        /// porque ao executar o construtor ainda existem objetos não criados.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Zoom_Loaded(object sender, RoutedEventArgs e)
        {
            text = GetTextControl();
            if (text != null)
            {
                text.LostFocus += new RoutedEventHandler(text_LostFocus);
                text.GotFocus += new RoutedEventHandler(text_GotFocus);
                text.PreviewKeyDown += new KeyEventHandler(text_PreviewKeyDown);
            }

            IsZoomTextFocused = false;
        }

        /// <summary>
        /// Evento para tratar enter no textobox onde o uduário pode digitar o zoom.
        /// Ele impede que sejam digitados valores acima ou abaixo de MaxValue e MinValue, respectivamente.
        /// Também impede que não seja digitado valor (assume MinValue).
        /// Obs: O tema deve definir MinValue e MaxValue.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void text_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                Int16TextBox text = (Int16TextBox)sender;
                if (text.Value == null)
                    text.Value = text.MinValue;
                else
                {
                    if (text.Value < text.MinValue)
                        text.Value = text.MinValue;
                    else
                    {
                        if (text.Value > text.MaxValue)
                            text.Value = text.MaxValue; 
                    }
                }
                CurrentZoom = (int)text.Value;
                text.Select(0, text.Text.Length);

                e.Handled = true;
            }
        }

        /// <summary>
        /// LostFocus do texto de digitação do zoom, seta propriedade e repassa evento para Zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void text_LostFocus(object sender, RoutedEventArgs e)
        {
			IsZoomTextFocused = false;
			if (LostFocusZoomText != null)
				LostFocusZoomText(sender, e);
			if (ZoomChanged != null)
				ZoomChanged(new ZoomChangedEventArgs(text.Value.Value));
        }

        /// <summary>
        /// GotFocus do texto de digitação do zoom, seta propriedade e repassa evento para Zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void text_GotFocus(object sender, RoutedEventArgs e)
        {
			IsZoomTextFocused = true;
			if (GotFocusZoomText != null)
				GotFocusZoomText(sender, e);
        }

        /// <summary>
        /// MouseLeave do Zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Zoom_MouseLeave(object sender, MouseEventArgs e)
        {
            this.KeepShowing = false;
        }

        /// <summary>
        /// MouseEnter do Zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Zoom_MouseEnter(object sender, MouseEventArgs e)
        {
            this.KeepShowing = true;
        }

        /// <summary>
        /// Evento associado aos botões para setar o zoom. Eles alteram o valor de CurrentZoom de acordo com o 
        /// CommandParameter definido no tema para o Zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ZoomButtonClick(object sender, ExecutedRoutedEventArgs e)
        {
            int zoom = -1;
            if (e.Parameter != null)
            {
                zoom = Convert.ToInt32(e.Parameter);
                CurrentZoom = zoom;

				if (ZoomChanged != null)
					ZoomChanged(new ZoomChangedEventArgs(zoom));
            }
        }

        /// <summary>
        /// Evento de click do botão restaurar. Serve para resetar as definições de zoom das janelas, fazendo com que todas 
        /// passem a assumir o zoom geral da aplicação.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void RestoreClick(object sender, ExecutedRoutedEventArgs e)
        {
			// Reseta o zoom para 100%
			this.CurrentZoom = 100;

            // Executa a atualização das configurações salvas para Window, colocando zoom como não definido
            SetSavedZoomConfigurations();
            //As janelas abertas precisam receber sinalizaçao (via mensagem) para que seus zooms sejam rastaurados (irão assumir o
            // atual zoom da aplicação).
            SendMessageToResetZoom();

			if (ZoomChanged != null)
				ZoomChanged(new ZoomChangedEventArgs(CurrentSettings.GetInstance().CurrentZoom));
        }

        /// <summary>
        /// Obtém o controle para digitação do zoom (PART_Text)
        /// </summary>
        /// <returns></returns>
        private Int16TextBox GetTextControl()
        {
            DependencyObject text = this.GetTemplateChild("PART_Text");
            if (text != null)
            {
                return (Int16TextBox)text;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Seta as configurações de zoom das janelas para não definidas. Utilizado paela função restaurar.
        /// </summary>
        private void SetSavedZoomConfigurations()
        {
            ProviderConfigurationSection configSection = ApplicationConfiguration.GetCurrent().GetSection<ProviderConfigurationSection>("clientProviders");

            IConfigurationSettingsHandler<ConfigurationObjectType> settingsHandler = ProviderLoader<IConfigurationSettingsHandler<ConfigurationObjectType>>.LoadProvider(configSection.Providers, LOCAL_SETTINGS_HANDLER_PROVIDER);

            settingsHandler.ChangeAllPropertiesValue<WindowLayoutSettings>(ConfigurationObjectType.Window, ZOOM_PROPERTY_NAME, ZOOM_NOT_DEFINED);

            settingsHandler = null;
        }

        /// <summary>
        /// Envia mensagem indicando que o zoom das janelas foi resetado. Serve para sinalizar às janelas abertas
        /// que elas devem assumir o zoom da aplicação.
        /// </summary>
        private void SendMessageToResetZoom()
        {
            //InternalMessage message = new InternalMessage();
            //message.MessageId = MessageIds.Application.SNC_APPLICATION_ZOOM_RESETED;
            //message.Message = CurrentSettings.GetInstance().CurrentZoom;

            //WPFMessageManager messageManager = new WPFMessageManager();
            //messageManager.SendMessage(message);
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			base.OnPropertyChanged(e);
			if (e.Property.Name == "CurrentZoom")
			{
				if (ZoomChanged != null)
					ZoomChanged(new ZoomChangedEventArgs(Convert.ToInt16(e.NewValue)));
			}
		}        
    }
}